﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StoreTransport.Data;
using StoreTransport.DAO;
using StoreTransport.Business.Helper;

namespace StoreTransport.Business.Services {
    class AllocatedService : IAllocatedService {
        public List<MarketData> AllocateStore(StoreTransportDataContext context, ProductLine pl, List<int> RegionIds,
            List<TransportData> transports) {
            List<MarketProductImport> listProductOfMarkets = context.MarketProductImports
                                                        .Where(r => pl.ID == r.ProductLineId
                                                        && (r.StoreAmount > 0 || r.SaleRate > 0)
                                                        && (RegionIds.Count == 0 || RegionIds.Contains(r.RegionId)))
                                                        .ToList();
            Dictionary<int, Dictionary<int, int>> marketTo = new Dictionary<int, Dictionary<int, int>>();
            Dictionary<int, Dictionary<int, int>> marketFrom = new Dictionary<int, Dictionary<int, int>>();
            if (transports.Count > 0) {
                foreach (TransportData t in transports) {
                    int productkindId = t.productKindId;
                    if (!marketTo.ContainsKey(t.marketIdTo)) {
                        marketTo.Add(t.marketIdTo, new Dictionary<int, int>());
                    }
                    if (!marketFrom.ContainsKey(t.marketIdFrom)) {
                        marketFrom.Add(t.marketIdFrom, new Dictionary<int, int>());
                    }
                    Dictionary<int, int> productTos = marketTo[t.marketIdTo];
                    if (!productTos.ContainsKey(productkindId)) {
                        productTos.Add(productkindId, 0);
                    }
                    productTos[productkindId] += t.numberTransport;

                    Dictionary<int, int> productFroms = marketFrom[t.marketIdFrom];
                    if (!productFroms.ContainsKey(productkindId)) {
                        productFroms.Add(productkindId, 0);
                    }
                    productFroms[productkindId] += t.numberTransport;
                }
            }
            List<MarketData> markets = Calculator.BuildMarketDto(listProductOfMarkets, pl, true, marketTo, marketFrom);
            Dictionary<int, int> AmountOfMarkets = Calculator.ComputeAllAmounts(markets, pl.Package);
            List<ProductOrder> listProductOfCenter = context.ProductOrders.Where(ro => pl.ID == ro.ProductLineId 
                                                        && (ro.ImportInventory > 0 || ro.SumSaleRate > 0)).ToList();

            StoreCenterData storeCenter = ComputeStoreCenter(listProductOfCenter);
            if (storeCenter != null) {
                foreach (int ProductKindId in AmountOfMarkets.Keys) {
                    if (storeCenter.DicProductKindData.ContainsKey(ProductKindId)) {
                        ProductKindData productKind = storeCenter.DicProductKindData[ProductKindId];
                        if (productKind.ComputedInventory <= 0) {
                            // don't allocate when market over or enough product to sell or center have not got the product
                            continue;
                        }
                        if (AmountOfMarkets[ProductKindId] <= productKind.ComputedInventory) {
                            // If center enough product, need to transfer which product will be allocated
                            foreach (MarketData market in markets) {
                                if (market.DicProductKinds.ContainsKey(ProductKindId)) {
                                    executeAllocation(storeCenter, ProductKindId, market);
                                }
                            }
                        } else {
                            // don't enough products
                            recalculateInventoryList(context, markets, ProductKindId, storeCenter, pl);
                        }
                    }
                }
            }
            return markets;
        }

        private void executeAllocation(StoreCenterData storeCenter, int ProductKindId, MarketData market) {
            ProductKindData productKind = storeCenter.DicProductKindData[ProductKindId];
            int Amount = market.DicProductKinds[ProductKindId].ComputedStableProducts;
            if (Amount > 0) {
                market.DicProductKinds[ProductKindId].Amount = Amount;
                productKind.ComputedInventory -= Amount;
                // reset Amount to sure product processed.
                market.DicProductKinds[ProductKindId].ComputedStableProducts = 0;
                // transfer from kind to code
                TransferProductKindToProductCode(storeCenter, ProductKindId, market, Amount);
            }
        }


        private void recalculateInventoryList(StoreTransportDataContext context,
            List<MarketData> markets, int productKindId, StoreCenterData central, ProductLine productLine) {

            var needComputedMarkets = markets.Where(c => c.DicProductKinds.ContainsKey(productKindId) &&
                 c.DicProductKinds[productKindId].ComputedStableProducts > 0);
            // get sum of kind of markets

            //So ngay chuan 
            int stableDay = productLine.StableSellDays;
            //So con trong kho
            // get sum of kind of store center
            var sumStoreQuatity = central.DicProductKindData[productKindId].SumCurrentQuatity;
            // tong suc ban
            var sumSaleRate = central.DicProductKindData[productKindId].SaleRate;
            //tong so luong can
            var sumMarketQuatity = needComputedMarkets.Where(c => c.DicProductKinds[productKindId].ComputedStableProducts > 0).
                Sum(c => c.DicProductKinds[productKindId].ComputedStableProducts);

            // so ngay ban binh quan
            double avagreDays = stableDay - (sumMarketQuatity - sumStoreQuatity) / (double)sumSaleRate;
            avagreDays = CommonHelper.roundedDown(avagreDays);

            // so luong con lai cua kho sau khi da phan bo theo ngay binh qua. 
            // so luong nay khong du de nang ngay ban hang cua cac sieu thi thieu hang len them 1 ngay nua
            //  so luong nay se duoc chia cho cac sieu thi theo uu tien la suc ban lon nhat.

            var sortedBySaleRateMarkets = needComputedMarkets.OrderBy(
                c => c.DicProductKinds[productKindId].SaleRate);

            var package = productLine.Package;
            int quantityStoreLeft = sumStoreQuatity;
            foreach (var market in sortedBySaleRateMarkets) {
                ProductKindData kindData = market.DicProductKinds[productKindId];
                var amountNeed = kindData.ComputedStableProducts;
                var sellDaysLeft = CommonHelper.roundedDown((kindData.SumCurrentQuatity / kindData.SaleRate));
                if (amountNeed > 0 && (avagreDays - sellDaysLeft) >= 0) {
                    kindData.ComputedStableProducts = 0;
                    var quantityNeeded = Calculator.
                           ComputedNeedProduct(kindData.SaleRate, kindData.SumCurrentQuatity, stableDay);
                    quantityNeeded = Convert.ToInt32(quantityNeeded / (double)package + 0.5) * package;
                    if (quantityStoreLeft - quantityNeeded > 0) {
                        kindData.ComputedStableProducts = quantityNeeded;
                        quantityStoreLeft -= quantityNeeded;
                    } else {
                        kindData.ComputedStableProducts = quantityStoreLeft;
                        executeAllocation(central, productKindId, market);
                        break;
                    }
                    executeAllocation(central, productKindId, market);
                }
            }
        }

        private StoreCenterData ComputeStoreCenter(List<ProductOrder> orders) {
            StoreCenterData result = new StoreCenterData();
            foreach (ProductOrder order in orders) {
                int ProductKindId = order.ProductKindId;
                double SaleRate = order.SumSaleRate;
                order.ComputedInventory = order.ImportInventory;// revert data for calculate
                int QuantityLeft = order.ComputedInventory;
                ProductKindData pk = new ProductKindData();
                if (!result.DicProductKindData.ContainsKey(ProductKindId)) {
                    pk.ProductKindId = ProductKindId;
                    result.DicProductKindData.Add(ProductKindId, pk);
                }
                
                pk = result.DicProductKindData[ProductKindId];
                if (!pk.DicProducts.ContainsKey(order.ProductId)) {
                    pk.DicProducts.Add(order.ProductId, new ProductData());
                }
                ProductData p = pk.DicProducts[order.ProductId];
                p.SaleRate = SaleRate;
                p.ImportInventory = QuantityLeft;
                p.ComputedInventory = QuantityLeft;
                p.ProductId = order.ProductId;
                pk.SaleRate += SaleRate;
                pk.ImportInventory += QuantityLeft;
                pk.ComputedInventory = pk.ImportInventory;
            }
            return result;
        }


        private void TransferProductKindToProductCode(StoreCenterData centralStore,
            int ProductKindId, MarketData market, int Amount) {
            if (market.DicProductKinds.ContainsKey(ProductKindId)) {
                ProductKindData productKind = market.DicProductKinds[ProductKindId];
                List<ProductData> rs = centralStore.GetPriorityList(ProductKindId);
                foreach (ProductData productOrder in rs) {
                    if (Amount <= 0)
                        return; // allocate complete.
                    if (productOrder.ComputedInventory <= 0)
                        continue;
                    int productId = productOrder.ProductId;
                    Product p = CommonHelper.ProductList[productId];
                    ProductKind kind = p.ProductKind1;
                    if (!productKind.DicProducts.ContainsKey(productId)) {
                        ProductData newProduct = new ProductData();
                        newProduct.ProductCode = p.ProductCode;
                        newProduct.ProductLineName = CommonHelper.CurrentProductLine.ProductLineName;
                        newProduct.ProductName = p.ProductName;
                        newProduct.ImportInventory = 0;
                        productKind.DicProducts.Add(productId, newProduct);
                    }
                    ProductData product = productKind.DicProducts[productId];
                    if (productOrder.ComputedInventory >= Amount) {
                        product.ComputedInventory += Amount;
                        productOrder.ComputedInventory -= Amount;
                        product.Amount = Amount;
                        Amount = 0;
                    } else {
                        product.ComputedInventory += productOrder.ComputedInventory;
                        Amount = Amount - productOrder.ComputedInventory;
                        product.Amount = productOrder.ComputedInventory;
                        productOrder.ComputedInventory = 0;
                    }
                    productOrder.Amount = productOrder.ImportInventory - productOrder.ComputedInventory;
                }
            }
        }
    }
}
